package utilities;

import java.util.*;

public class JSONGenerator implements Generator {
	// Instance Variables
	private StringBuilder json;
	
	
	// Constants
	private static final String QUOTATION_MARK = "\"";
	
	
	// Constructor
	/**
	 * Constructor: creates a new StringBuilder
	 */
	public JSONGenerator() {
		this.json = new StringBuilder();
	}
	
	
	// JSON Generation
	/**
	 * Generates the JSON String for the specified Archive
	 */
	public String generate(Archive archive) {
		// Clear the builder
		this.json.setLength(0);
		
		// Generate the string
		this.generateArchive((JSONArchive)archive);
		
		return this.json.toString();
	}
	
	/**
	 * Tests if the specified map is not null, and has at least one key-value pair
	 */
	private static boolean mapIsValid(Map<?, ?> map) {
		return map != null && map.keySet().size() > 0;
	}
	
	/**
	 * Recursive method: Parses an Archive and adds values to the StringBuilder
	 * @param archive
	 */
	private void generateArchive(JSONArchive archive) {
		// Get hashmaps
		Map<String, String> attributes = archive.attributes;
		Map<String, List<String>> attributeLists = archive.attributeLists;
		Map<String, Archive> ones = archive.oneRelationships;
		Map<String, List<Archive>> manys = archive.manyRelationships;
		
		// Add opening brace
		this.json.append('{');
		
		// Add attributes
		if (attributes != null && attributes.keySet().size() > 0) {
			this.json.append("\"" + JSONArchive.ATTRIBUTES_KEY + "\":");
			
			this.json.append("{");
			for (String key : attributes.keySet()) {
				String value = attributes.get(key);
				this.json.append(QUOTATION_MARK + key + QUOTATION_MARK + ":" + QUOTATION_MARK + value + QUOTATION_MARK + ",");
			}
			this.json.deleteCharAt(this.json.length() - 1); // delete last comma
			this.json.append("}");
			
			if (mapIsValid(attributeLists) || mapIsValid(ones) || mapIsValid(manys)) {
				this.json.append(',');
			}
		}
		
		// Add attribute lists
		if (attributeLists != null && attributeLists.keySet().size() > 0) {
			this.json.append("\"" + JSONArchive.ATTRIBUTE_LISTS_KEY + "\":");
			
			this.json.append("{");
			for (String key : attributeLists.keySet()) {
				List<String> value = attributeLists.get(key);
				this.json.append(QUOTATION_MARK + key + QUOTATION_MARK + ":");
				this.generateAttributeList(value);
				this.json.append(",");
			}
			this.json.deleteCharAt(this.json.length() - 1); // delete last comma
			this.json.append("}");
			
			if (mapIsValid(ones) || mapIsValid(manys)) {
				this.json.append(',');
			}
		}
		
		// Add ones
		if (ones != null && ones.keySet().size() > 0) {
			this.json.append("\"" + JSONArchive.ONE_KEY + "\":");
			
			this.json.append("{");
			for (String key : ones.keySet()) {
				JSONArchive value = (JSONArchive)ones.get(key);
				this.json.append(QUOTATION_MARK + key + QUOTATION_MARK + ":");
				this.generateArchive(value);
				this.json.append(",");
			}
			this.json.deleteCharAt(this.json.length() - 1); // delete last comma
			this.json.append("}");
			
			if (mapIsValid(manys)) {
				this.json.append(',');
			}
		}
		
		// Add manys
		if (manys != null && manys.keySet().size() > 0) {
			this.json.append("\"" + JSONArchive.MANY_KEY + "\":");
			
			this.json.append("{");
			for (String key : manys.keySet()) {
				List<Archive> value = manys.get(key);
				this.json.append(QUOTATION_MARK + key + QUOTATION_MARK + ":");
				this.generateArchiveList(value);
				this.json.append(",");
			}
			this.json.deleteCharAt(this.json.length() - 1); // delete last comma
			this.json.append("}");
		}
		
		// Add opening brace
		this.json.append('}');
	}
	
	/**
	 * Generates JSON for a List of Strings
	 * @param list
	 */
	private void generateAttributeList(List<String> list) {
		// Add opening bracket
		this.json.append("[");
		
		for (String string : list) {
			this.json.append(QUOTATION_MARK + string + QUOTATION_MARK);
			this.json.append(",");
		}
		this.json.deleteCharAt(this.json.length() - 1);
		
		// Add closing bracket
		this.json.append("]");
	}
	
	/**
	 * Generates JSON for a List of Archive objects
	 * @param list
	 */
	private void generateArchiveList(List<Archive> list) {
		// Add opening bracket
		this.json.append("[");
		
		for (Archive archive : list) {
			this.generateArchive((JSONArchive)archive);
			this.json.append(",");
		}
		this.json.deleteCharAt(this.json.length() - 1);
		
		// Add closing bracket
		this.json.append("]");
	}
	
	
//	public static void main(String args[]) {
//		Archive archive = new JSONArchive();
//		List<String> list = new ArrayList<String>();
//		list.add("one");
//		list.add("two");
//		list.add("three");
//		archive.putAttributeList("numbers", list);
//		
//		List<TestStat> stats = new ArrayList<TestStat>(3);
//		stats.add(new TestStat());
//		stats.add(new TestStat());
//		stats.add(new TestStat());
//		archive.putManyRelationship("stats", stats);
//		
//		Generator generator = new JSONGenerator();
//		String json = generator.generate(archive);
//		System.out.println("generated JSON: " + json);
//		
////		String json = "{\"many\":{\"stats\":[{\"attributes\":{\"name\":\"statName\",\"value\":\"100.0\"}},{\"attributes\":{\"name\":\"statName\",\"value\":\"100.0\"}},{\"attributes\":{\"name\":\"statName\",\"value\":\"100.0\"}}]}}";
//		
//		Parser parser = new JSONParser(json);
//		Archive loaded = parser.parse();
//		System.out.print("loaded archive: " + loaded);
//	}
	
}
